//
//  UICollectionViewFlowLayout+EmptyView.swift
//  ltsp
//
//  Created by jyvyi_ios on 2022/8/8.
//

import Foundation
import UIKit

protocol PYSectionEmptyViewDataSource{
    
    func emptyDataSourceView(for collectionView:UICollectionView?,at section:Int) -> UICollectionReusableView.Type?
    
    func sizeOfEmptyDataSourceView(for collectionView:UICollectionView?, at section:Int) -> CGSize?
}

class PYWeakObjectContainer : NSObject{
 
    weak var weakObject: AnyObject?
    
    init(with weakObject: Any?) {
        super.init()
        self.weakObject = weakObject as AnyObject?
    }
}

private var kEmptyDataSetSource = "emptyDataSetSource"

extension UICollectionViewFlowLayout{
    
    var emptyDataSource : PYSectionEmptyViewDataSource?{
        
        set{
            if newValue == nil{
                
                self.invalidate()
            }
            objc_setAssociatedObject(self, &kEmptyDataSetSource, PYWeakObjectContainer(with: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            UICollectionViewFlowLayout.swizzleMethods
        }
        get{
            let container = objc_getAssociatedObject(self, &kEmptyDataSetSource) as? PYWeakObjectContainer
            return container?.weakObject as? PYSectionEmptyViewDataSource
        }
    }
    
    func invalidate(){
        
    }
    
    private static let swizzleMethods: () = {
        
        let flowOriginPrepareSel = #selector(UICollectionViewLayout.layoutAttributesForElements(in:))
        let flowSwizzlePrepareSel = #selector(UICollectionViewFlowLayout.flowSwizzleLayoutAttributesForElements(in:))
        
        swizzleMethod(for: UICollectionViewFlowLayout.self, originalSelector: flowOriginPrepareSel, swizzledSelector: flowSwizzlePrepareSel)
    }()
    
    @objc func flowSwizzleLayoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?{
        
        let supAtts = flowSwizzleLayoutAttributesForElements(in: rect)
        var atts : [UICollectionViewLayoutAttributes] = []
        
        if let supAtts : [UICollectionViewLayoutAttributes] = supAtts {
            atts = NSMutableArray.init(array: supAtts) as! [UICollectionViewLayoutAttributes]
        }
        
        let sectionsNum = self.collectionView?.numberOfSections ?? 0
        for section in 0..<sectionsNum {
            let rowsNum = self.collectionView?.numberOfItems(inSection: section) ?? 0
            guard rowsNum <= 0 else {
                continue
            }
            guard let emptyViewAtt = self.emptyLayoutAttributesForDecorationView(at: section) else {
                continue
            }
            atts.append(emptyViewAtt)
        }
        
        return atts
    }
    private func emptyLayoutAttributesForDecorationView(at section:Int) -> UICollectionViewLayoutAttributes?{
        
        guard let emptyClassName = self.emptyDataSource?.emptyDataSourceView(for: self.collectionView, at: section) else {
            return nil
        }
        guard let emptySize = self.emptyDataSource?.sizeOfEmptyDataSourceView(for: self.collectionView, at:section) else {
            return nil
        }
        let identifier = self.emptyIdentifier(at: section)
        self.register(emptyClassName, forDecorationViewOfKind: identifier)
        
        let indexPath = IndexPath.init(row: 0, section: section)
        let att = UICollectionViewLayoutAttributes.init(forDecorationViewOfKind: identifier, with: indexPath)
        att.zIndex = -1
        
        let frame_Y = self.emptyDataSourceViewMinY(at: section)
        att.frame = CGRect.init(x: 0, y: frame_Y, width: emptySize.width, height: emptySize.height)
        return att
    }
    
    private func emptyDataSourceViewMinY(at section:Int) -> CGFloat{
        
        var minY : CGFloat = 0
        let indexPath = IndexPath.init(row: 0, section: section)
        if let headerAtt = self.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: indexPath){
            minY = headerAtt.frame.maxY
        }
        else if section > 0{
            
            var lastIndexPath : IndexPath?
            if let sectionNum = self.collectionView?.numberOfSections{
                for mapSection in 0..<sectionNum {
                    let sortSection = sectionNum - mapSection
                    if sortSection >= section{
                        continue
                    }
                    if let rowsNum = self.collectionView?.numberOfItems(inSection: sortSection){
                        lastIndexPath = IndexPath.init(row: rowsNum - 1, section: sortSection)
                        break
                    }
                }
            }
            if let lastIndexPath = lastIndexPath ,let att = self.layoutAttributesForItem(at: lastIndexPath){
                minY = att.frame.maxY
            }
        }
        return minY
    }
    private func emptyIdentifier(at section:Int) -> String{
        
        return "identifierForEmptyDataSourceAt\(section)"
    }
    private class func swizzleMethod(for aClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
        let originalMethod = class_getInstanceMethod(aClass, originalSelector)
        let swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector)
        
        let didAddMethod = class_addMethod(aClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
        
        if didAddMethod {
            class_replaceMethod(aClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
        } else {
            method_exchangeImplementations(originalMethod!, swizzledMethod!)
        }
    }
}
